#include <direct.h> // for getcwd
//#include <stdlib.h>// for MAX_PATH
//#include <iostream> // for cout and cin

#include "supergl.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

#include "constantes.h"
#include "orbit.h"
#include "text.h"


double time_old = 0.0;
double atm = 1.0, year = 1.0;

float camx = 0.0f;
float camy = 0.0f;
float camz = 0.0f;
float zoom = 16.0;
float nextzoom = 16.0;

float rotx = -25.0;
float roty = 0.0f;

int lastx = 0;
int lasty = 0;
unsigned char Buttons[3] = {0};
int modifier = 0;

// Escala
int planet_scale = 1000.0;
int star_scale = 60.0;
double time_scale = 0.001;



// sunshine 
GLfloat luz_specular0[] = {C0S, C0S, C0S, 1.0};
GLfloat luz_difuso0[] = {C0D, C0D, C0D, 1.0};
GLfloat luz_ambiente0[] = {C0A, C0A, C0A, 1.0};
GLfloat luz_pos0[] = {0, 0, 0, 1.0};

GLfloat luz_specular3[] = {C3S, C3S, C3S, 1.0};
GLfloat luz_difuso3[] = {C3D, C3D, C3D, 1.0};
GLfloat luz_ambiente3[] = {C3A, C3A, C3A, 1.0};
GLfloat luz_pos3[] = {0, 0, 0, 1.0};


// Directional white light from the camera
GLfloat luz_specular1[] = {C1S, C1S, C1S, 1.0};
GLfloat luz_difuso1[] = {C1D, C1D, C1D, 1.0};
GLfloat luz_ambiente1[] = {C1A, C1A, C1A, 1.0};
GLfloat luz_pos1[] = {0, 0, DIST, 1.0};

GLfloat luz_specular2[] = {C2S, C2S, C2S, 1.0};
GLfloat luz_difuso2[] = {C2D, C2D, C2D, 1.0};
GLfloat luz_ambiente2[] = {C2A, C2A, C2A, 1.0};
GLfloat luz_pos2[] = {0, 0, DIST, 1.0};


void init(void) {

    // Load information of planets
    loadPlanetInfo();
    setIncT(1.024);

    // Configure features
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    glShadeModel(GL_SMOOTH);
    glClearColor(0.0, 0.0, 0.0, 0.0);

    // Transparency
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


    // Lights
    glLightfv(GL_LIGHT0, GL_DIFFUSE, luz_difuso0);
    glLightfv(GL_LIGHT0, GL_SPECULAR, luz_specular0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, luz_ambiente0);
    glLightfv(GL_LIGHT0, GL_POSITION, luz_pos0);

    glLightfv(GL_LIGHT3, GL_DIFFUSE, luz_difuso3);
    glLightfv(GL_LIGHT3, GL_SPECULAR, luz_specular3);
    glLightfv(GL_LIGHT3, GL_AMBIENT, luz_ambiente3);
    glLightfv(GL_LIGHT3, GL_POSITION, luz_pos3);

    glLightfv(GL_LIGHT1, GL_DIFFUSE, luz_difuso1);
    glLightfv(GL_LIGHT1, GL_SPECULAR, luz_specular1);
    glLightfv(GL_LIGHT1, GL_AMBIENT, luz_ambiente1);
    glLightfv(GL_LIGHT1, GL_POSITION, luz_pos1);

    glLightfv(GL_LIGHT2, GL_DIFFUSE, luz_difuso2);
    glLightfv(GL_LIGHT2, GL_SPECULAR, luz_specular2);
    glLightfv(GL_LIGHT2, GL_AMBIENT, luz_ambiente2);
    glLightfv(GL_LIGHT2, GL_POSITION, luz_pos2);

    // Turn lights
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHT1);
    glEnable(GL_LIGHTING);

}



void camera() {
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, zoom, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}


void drawRing(int n, float minr, float maxr)
{
	int i;
	float theta;
	float x,y,z;

	glDisable( GL_CULL_FACE );
	glBegin(GL_QUAD_STRIP);

	for(i=0;i<=n;i++) {
		theta = i * 2 * PI / n;

		x= sin( theta );
		y=0.0;
		z= cos( theta );

		glNormal3f( x, y, z);
		glTexCoord2f( 0.0, 0.5);     // Horizontal
		glVertex3f( x *minr, y*minr, z*minr );


		glNormal3f( x, y, z);
		glTexCoord2f( 1.0, 0.5);
		glVertex3f( x *maxr, y*maxr, z*maxr );
	}
	glEnd();
	glEnable( GL_CULL_FACE );
}

void display(void) {
    int i;
    float textdist;



    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // set camera
    camera();

    // Place lights from the camera
    glLightfv(GL_LIGHT1, GL_POSITION, luz_pos1);
    glLightfv(GL_LIGHT2, GL_POSITION, luz_pos2);

    glPushMatrix();

    // Rotate camera
    glRotatef(-rotx, 1, 0, 0);
    glRotatef(roty, 0, 1, 0);
    glTranslatef(-camx, -camz, -camy);

    // Place light from the Sun
    glLightfv(GL_LIGHT0, GL_POSITION, luz_pos0);
    glLightfv(GL_LIGHT3, GL_POSITION, luz_pos3);

    glEnable(GL_LIGHTING);


    glPushMatrix();
    glScalef(TRIP(getStarRadius() * star_scale));

    glutSolidSphere(1, 50, 50);
    glPopMatrix();
    glDisable(GL_LIGHT2);

    // draw planet
    for (i = 0; i < getNumPlanet(); i++) {
        glPushMatrix();

        glTranslatef(getX(i), getZ(i), getY(i)); // Traslacion

        glScalef(TRIP(getPlanetRadius(i) * planet_scale)); // Size of the planet

        if (getPlanetRadius(i) > MIN_PLANETRAD_LIGHT3) {
            glEnable(GL_LIGHT3);
        }


        glutSolidSphere(1, 50, 50);
 
        if (i == 5) //ring for Saturn
        {
            drawRing(50, 1.5, 2.4);
        }

        if (getPlanetRadius(i) > MIN_PLANETRAD_LIGHT3) {
            glDisable(GL_LIGHT3);
        }

        glPopMatrix();
    }

    glPopMatrix();

    // Show labels for each planet

    for (i = 0; i < getNumPlanet(); i++) {
        glPushMatrix();

        // Distancia de la etiqueta respecto al centro del planeta.
        textdist = getPlanetRadius(i) * planet_scale * 1.1 + 0.5 * zoom / MAX_ZOOM;
        glTranslatef(textdist, 0, 0);

        // Rotation of the camera
        glRotatef(-rotx, 1, 0, 0);
        glRotatef(roty, 0, 1, 0);

        // Follow planet
        glTranslatef(-camx, -camz, -camy);


        glTranslatef(getX(i), getZ(i), getY(i));

        text(getPlanetName(i), 0, 0, 0);
        glPopMatrix();
    }


    glutSwapBuffers();
}



void Orbitar() {
    int i;
    for (i = 0; i < getNumPlanet(); i++) {
        mover(i);
    }
    glutPostRedisplay();
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat) w / (GLfloat) h, 0.05, 1000.0);

    camera();
}


void keyPressed(unsigned char key, int key_x, int key_y) {

    switch (key) {
        case 'T':
            time_scale = getIncT() * 2.0;
            if (time_scale > 8388.608)
                time_scale = 8388.608;
            setIncT(time_scale);
            glutPostRedisplay();
            break;
        case 't':
            time_scale = getIncT() / 2.0;
            if (time_scale < 0.001)
                time_scale = 0.001;
            setIncT(time_scale);
            glutPostRedisplay();
            break;
        case 27: // Escape
            exit(0);
    }
}


void move_rotation(int x, int y) {
    int diffx = x - lastx;
    int diffy = y - lasty;
    lastx = x;
    lasty = y;

    if (Buttons[0] && !modifier) {
        rotx += (float) 0.5f * diffy;
        roty += (float) 0.5f * diffx;
    } else if (Buttons[1] || modifier) {
        zoom += (float) 0.005 * diffy * (abs(zoom / 2) + 1);
        if (zoom > MAX_ZOOM) zoom = MAX_ZOOM;
        nextzoom = zoom;
    }

    glutPostRedisplay();

}

void click_rotation(int b, int s, int x, int y) {
    lastx = x;
    lasty = y;

    modifier = glutGetModifiers();
    switch (b) {
        case GLUT_LEFT_BUTTON:
            Buttons[0] = ((GLUT_DOWN == s) ? 1 : 0);
            break;
        case GLUT_MIDDLE_BUTTON:
            Buttons[1] = ((GLUT_DOWN == s) ? 1 : 0);
            break;
        case GLUT_RIGHT_BUTTON:
            Buttons[2] = ((GLUT_DOWN == s) ? 1 : 0);
            break;
        case GLUT_WHEEL_UP:
            if (zoom > 1) zoom -= 0.15;
            camera();
            break;
        case GLUT_WHEEL_DOWN:
            if (zoom <= 100) zoom += 0.15;
            camera();
            break;
    }
    glutPostRedisplay();
}


void timer(int value) {
    Orbitar();
    glutPostRedisplay();
    glutTimerFunc(TIMER_DELAY, timer, 1);
}


int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_ALPHA | GLUT_DEPTH);
    glutInitWindowSize(900, 700);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(WINDOW_TITLE);

    init();

    glutDisplayFunc(display);
    glutIdleFunc(Orbitar);
    glutMouseFunc(click_rotation);
    glutMotionFunc(move_rotation);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyPressed);

    glutMainLoop();
    return 0;
}
